home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 September / PCWorld_2008-09_cd.bin / v cisle / sadanastroju / lightning-0.8-tb-win.xpi / chrome / calendar.jar / content / calendar / calendar-task-tree.xml < prev    next >
Extensible Markup Language  |  2008-03-03  |  48KB  |  1,167 lines

  1. <?xml version="1.0"?>
  2. <!-- ***** BEGIN LICENSE BLOCK *****
  3.    - Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.    -
  5.    - The contents of this file are subject to the Mozilla Public License Version
  6.    - 1.1 (the "License"); you may not use this file except in compliance with
  7.    - the License. You may obtain a copy of the License at
  8.    - http://www.mozilla.org/MPL/
  9.    -
  10.    - Software distributed under the License is distributed on an "AS IS" basis,
  11.    - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.    - for the specific language governing rights and limitations under the
  13.    - License.
  14.    -
  15.    - The Original Code is OEone Calendar Code, released October 31st, 2001.
  16.    -
  17.    - The Initial Developer of the Original Code is OEone Corporation.
  18.    - Portions created by the Initial Developer are Copyright (C) 2001
  19.    - the Initial Developer. All Rights Reserved.
  20.    -
  21.    - Contributor(s):
  22.    -   Garth Smedley <garths@oeone.com>
  23.    -   Mike Potter <mikep@oeone.com>
  24.    -   Chris Charabaruk <coldacid@meldstar.com>
  25.    -   Colin Phillips <colinp@oeone.com>
  26.    -   ArentJan Banck <ajbanck@planet.nl>
  27.    -   Curtis Jewell <csjewell@mail.freeshell.org>
  28.    -   Eric Belhaire <eric.belhaire@ief.u-psud.fr>
  29.    -   Mark Swaffer <swaff@fudo.org>
  30.    -   Michael Buettner <michael.buettner@sun.com>
  31.    -   Philipp Kewisch <mozilla@kewis.ch>
  32.    -   Lars Wohlfahrt <thetux.moz@googlemail.com>
  33.    -
  34.    - Alternatively, the contents of this file may be used under the terms of
  35.    - either the GNU General Public License Version 2 or later (the "GPL"), or
  36.    - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  37.    - in which case the provisions of the GPL or the LGPL are applicable instead
  38.    - of those above. If you wish to allow use of your version of this file only
  39.    - under the terms of either the GPL or the LGPL, and not to allow others to
  40.    - use your version of this file under the terms of the MPL, indicate your
  41.    - decision by deleting the provisions above and replace them with the notice
  42.    - and other provisions required by the GPL or the LGPL. If you do not delete
  43.    - the provisions above, a recipient may use your version of this file under
  44.    - the terms of any one of the MPL, the GPL or the LGPL.
  45.    -
  46.    - ***** END LICENSE BLOCK ***** -->
  47.  
  48. <!DOCTYPE dialog [
  49.   <!ENTITY % dtd1 SYSTEM "chrome://calendar/locale/global.dtd" > %dtd1;
  50.   <!ENTITY % dtd2 SYSTEM "chrome://calendar/locale/calendar.dtd" > %dtd2;
  51. ]>
  52.  
  53. <bindings id="calendar-task-tree-bindings"
  54.   xmlns="http://www.mozilla.org/xbl"
  55.   xmlns:xbl="http://www.mozilla.org/xbl"
  56.   xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  57.  
  58.   <binding id="calendar-task-tree">
  59.     <resources>
  60.       <stylesheet src="chrome://calendar/skin/calendar-task-tree.css"/>
  61.     </resources>
  62.     <content>
  63.       <xul:tree anonid="calendar-task-tree"
  64.                 class="calendar-task-tree" 
  65.                 flex="1"
  66.                 enableColumnDrag="false">
  67.         <xul:treecols anonid="calendar-task-tree-cols">
  68.           <xul:treecol anonid="calendar-task-tree-col-completed"
  69.                        class="calendar-task-tree-col-completed" 
  70.                        minwidth="19"
  71.                        fixed="true"
  72.                        cycler="true"
  73.                        label="&calendar.unifinder.tree.done.label;">
  74.             <xul:image anonid="checkboximg" />
  75.           </xul:treecol>
  76.           <xul:splitter class="tree-splitter"/>
  77.           <xul:treecol anonid="calendar-task-tree-col-priority"
  78.                        class="calendar-task-tree-col-priority"
  79.                        minwidth="17"
  80.                        fixed="true"
  81.                        label="&calendar.unifinder.tree.priority.label;">
  82.             <xul:image anonid="priorityimg"/>
  83.           </xul:treecol>
  84.           <xul:splitter class="tree-splitter"/>
  85.           <xul:treecol anonid="calendar-task-tree-col-title"
  86.                        flex="1" 
  87.                        label="&calendar.unifinder.tree.title.label;"/>
  88.           <xul:splitter class="tree-splitter"/>
  89.           <xul:treecol anonid="calendar-task-tree-col-startdate"
  90.                        flex="1" label="&calendar.unifinder.tree.startdate.label;"/>
  91.           <xul:splitter class="tree-splitter"/>
  92.           <xul:treecol anonid="calendar-task-tree-col-duedate"
  93.                        flex="1" label="&calendar.unifinder.tree.duedate.label;"/>
  94.           <xul:splitter class="tree-splitter"/>
  95.           <xul:treecol anonid="calendar-task-tree-col-completeddate"
  96.                        flex="1" label="&calendar.unifinder.tree.completeddate.label;"/>
  97.           <xul:splitter class="tree-splitter"/>
  98.           <xul:treecol anonid="calendar-task-tree-col-percentcomplete"
  99.                        flex="1" label="&calendar.unifinder.tree.percentcomplete.label;"/>
  100.           <xul:splitter class="tree-splitter"/>
  101.           <xul:treecol anonid="calendar-task-tree-col-categories"
  102.                        flex="1" label="&calendar.unifinder.tree.categories.label;"/>
  103.           <xul:splitter class="tree-splitter"/>
  104.           <xul:treecol anonid="calendar-task-tree-col-location"
  105.                        label="&calendar.unifinder.tree.location.label;"/>
  106.           <xul:splitter class="tree-splitter"/>
  107.           <xul:treecol anonid="calendar-task-tree-col-status"
  108.                        flex="1"
  109.                        label="&calendar.unifinder.tree.status.label;"/>
  110.           <xul:splitter class="tree-splitter"/>
  111.           <xul:treecol anonid="calendar-task-tree-col-calendarname"
  112.                        flex="1"
  113.                        label="&calendar.unifinder.tree.calendarname.label;"/>
  114.         </xul:treecols>
  115.         <xul:treechildren tooltip="taskTreeTooltip"/>
  116.       </xul:tree>
  117.     </content>
  118.  
  119.     <implementation>
  120.       
  121.       <field name="mLoadCount">0</field>
  122.       <field name="mTaskArray">[]</field>
  123.       <field name="mHash2Index"><![CDATA[({})]]></field>
  124.       <field name="mContextTask">null</field>
  125.       <field name="mRefreshQueue">[]</field>
  126.       <field name="mPendingRefresh">null</field>
  127.       <field name="mHideCompletedTasks">false</field>
  128.       <field name="mFilterFunction">null</field>
  129.       <field name="mStartDate">null</field>
  130.       <field name="mEndDate">null</field>
  131.  
  132.       <property name="currentIndex">
  133.         <getter><![CDATA[
  134.           var tree = document.getAnonymousElementByAttribute(
  135.               this, "anonid", "calendar-task-tree");
  136.           return tree.currentIndex;
  137.         ]]></getter>
  138.       </property>
  139.  
  140.       <property name="currentTask">
  141.         <getter><![CDATA[
  142.           var tree = document.getAnonymousElementByAttribute(
  143.               this, "anonid", "calendar-task-tree");
  144.           var index = tree.currentIndex;
  145.           return (index < 0) ? null : this.mTaskArray[index];
  146.         ]]></getter>
  147.       </property>
  148.  
  149.       <property name="selectedTasks" readonly="true">
  150.         <getter><![CDATA[
  151.           var tasks = [];
  152.           var start = {};
  153.           var end = {};
  154.           if (!this.mTreeView.selection) {
  155.               return tasks;
  156.           }
  157.  
  158.           var rangeCount = this.mTreeView.selection.getRangeCount();
  159.           for (var range = 0; range < rangeCount; range++) {
  160.               this.mTreeView.selection.getRangeAt(range, start, end);
  161.               for (var i = start.value; i <= end.value; i++) {
  162.                   var task = this.getTaskAtRow(i);
  163.                   if (task) {
  164.                       tasks.push(this.getTaskAtRow(i));
  165.                   }
  166.               }
  167.           }
  168.           return tasks;
  169.         ]]></getter>
  170.       </property>
  171.  
  172.       <property name="hideCompleted">
  173.         <getter><![CDATA[
  174.           return this.mHideCompletedTasks;
  175.         ]]></getter>
  176.         <setter><![CDATA[
  177.           this.mHideCompletedTasks = val;
  178.           return val;
  179.         ]]></setter>
  180.       </property>
  181.       
  182.       <property name="filterFunction">
  183.         <setter><![CDATA[
  184.           this.mFilterFunction = val;
  185.           return val;
  186.         ]]></setter>
  187.       </property>
  188.       
  189.       <property name="startDate">
  190.         <getter><![CDATA[
  191.           return this.mStartDate;
  192.         ]]></getter>
  193.         <setter><![CDATA[
  194.           this.mStartDate = val;
  195.           return val;
  196.         ]]></setter>
  197.       </property>
  198.  
  199.       <property name="endDate">
  200.         <getter><![CDATA[
  201.           return this.mEndDate;
  202.         ]]></getter>
  203.         <setter><![CDATA[
  204.           this.mEndDate = val;
  205.           return val;
  206.         ]]></setter>
  207.       </property>
  208.       
  209.       <method name="getTaskAtRow">
  210.         <parameter name="aRow"/>
  211.         <body><![CDATA[
  212.           return this.mTaskArray[aRow];
  213.         ]]></body>
  214.       </method>
  215.  
  216.       <method name="getTaskFromEvent">
  217.         <parameter name="aEvent"/>
  218.         <body><![CDATA[
  219.           return this.mTreeView._getItemFromEvent(aEvent);
  220.         ]]></body>
  221.       </method>
  222.       
  223.       <property name="contextTask">
  224.         <getter><![CDATA[
  225.           return this.mContextTask;
  226.         ]]></getter>
  227.         <setter><![CDATA[
  228.           this.mContextTask = val;
  229.           return this.mContextTask;
  230.         ]]></setter>
  231.       </property>
  232.             
  233.       <field name="mTreeView"><![CDATA[
  234.       ({
  235.           /**
  236.            * Attributes
  237.            */
  238.  
  239.           // back reference to the binding
  240.           binding: this,
  241.           tree: null,
  242.           treebox: null,
  243.           selectedColumn: null,
  244.           sortDirection: null,
  245.           
  246.           // updated just before sort
  247.           sortStartedTime: now(),
  248.  
  249.           /**
  250.            * High-level task tree manipulation
  251.            */
  252.  
  253.           addItem: function tTV_addItem(aItem) {
  254.               if (aItem.isCompleted && this.binding.hideCompleted) {
  255.                   return;
  256.               }
  257.               var index = this.binding.mHash2Index[aItem.hashId];
  258.               if (index === undefined) {
  259.                   var index = this.binding.mTaskArray.length;
  260.                   this.binding.mTaskArray.push(aItem);
  261.                   this.binding.mHash2Index[aItem.hashId] = index;
  262.                   // The rowCountChanged function takes two arguments, the index where the
  263.                   // first row was inserted and the number of rows to insert.
  264.                   this.treebox.rowCountChanged(index, 1);
  265.                   this.tree.view.selection.select(index);
  266.               }
  267.               this.treebox.ensureRowIsVisible(this.rowCount - 1);
  268.           },
  269.  
  270.           removeItem: function tTV_removeItem(aItem) {
  271.               var index = this.binding.mHash2Index[aItem.hashId];
  272.               if (index != undefined) {
  273.                   delete this.binding.mHash2Index[aItem.hashId];
  274.                   this.binding.mTaskArray.splice(index, 1);
  275.                   this.treebox.rowCountChanged(index, -1);
  276.  
  277.                   if (index == this.rowCount) {
  278.                       index--;
  279.                   }
  280.  
  281.                   this.tree.view.selection.select(index);
  282.                   
  283.                   this.binding.recreateHashTable();
  284.               }
  285.           },
  286.  
  287.           modifyItem: function tTV_modifyItem(aNewItem, aOldItem) {
  288.               var index = this.binding.mHash2Index[aOldItem.hashId];
  289.               if (index != undefined) {
  290.                   // if a filter is installed we need to make sure that
  291.                   // the item still belongs to the set of valid items before
  292.                   // moving forward. if the filter cuts this item off, we
  293.                   // need to act accordingly.
  294.                   if (this.binding.mFilterFunction &&
  295.                       !this.binding.mFilterFunction(aNewItem)) {
  296.                       this.removeItem(aNewItem);
  297.                       return;
  298.                   }
  299.                   // same holds true for the completed filter, which is
  300.                   // currently modeled as an explicit boolean.
  301.                   if (aNewItem.isCompleted != aOldItem.isCompleted) {
  302.                       if (aNewItem.isCompleted && this.binding.hideCompleted) {
  303.                           this.removeItem(aNewItem);
  304.                           return;
  305.                       }
  306.                   }
  307.                   delete this.binding.mHash2Index[aOldItem.hashId];
  308.                   this.binding.mHash2Index[aNewItem.hashId] = index;
  309.                   this.binding.mTaskArray[index] = aNewItem;
  310.                   this.tree.view.selection.select(index);
  311.                   this.treebox.invalidateRow(index);
  312.               }
  313.           },
  314.  
  315.           clear: function tTV_clear() {
  316.               var count = this.binding.mTaskArray.length;
  317.               if (count > 0) {
  318.                   this.binding.mTaskArray = [];
  319.                   this.binding.mHash2Index = {};
  320.                   this.treebox.rowCountChanged(0, -count);
  321.                   this.tree.view.selection.clearSelection();
  322.               }
  323.           },
  324.           
  325.           updateItem: function tTV_updateItem(aItem) {
  326.               var index = this.binding.mHash2Index[aItem.hashId];
  327.               if (index) {
  328.                   this.treebox.invalidateRow(index);
  329.               }
  330.           },
  331.  
  332.           /**
  333.            * nsITreeView methods and properties
  334.            */
  335.  
  336.           get rowCount() {
  337.               return this.binding.mTaskArray.length;
  338.           },
  339.           
  340.           // An atomized list of properties for a given cell. Each property, x, that the
  341.           // view gives back will cause the pseudoclass :moz-tree-cell-x to be matched
  342.           // on the ::moz-tree-cell pseudoelement. It is use here to color a particular
  343.           // row with a color given by the progress state of the task.
  344.           getCellProperties: function mTV_getCellProperties(aRow, aCol, aProps) { 
  345.               var task = this.binding.mTaskArray[aRow];
  346.               if (aCol.element.getAttribute("anonid") == "calendar-task-tree-col-priority") {
  347.                   if(task.priority > 0 && task.priority < 5) {
  348.                       aProps.AppendElement(getAtomFromService("highpriority"));
  349.                   }
  350.                   if(task.priority > 5 && task.priority < 10) {
  351.                       aProps.AppendElement(getAtomFromService("lowpriority"));
  352.                   }
  353.               }
  354.               aProps.AppendElement(getAtomFromService(this._progressAtom(task)));
  355.               if (aCol.element.hasAttribute("anonid")) {
  356.                   aProps.AppendElement(getAtomFromService(aCol.element.getAttribute("anonid")));
  357.               }
  358.           },
  359.           
  360.           // Called to get properties to paint a column background.
  361.           // For shading the sort column, etc.
  362.           getColumnProperties: function mTV_getColumnProperties() {
  363.               return false;
  364.           },
  365.          
  366.           // An atomized list of properties for a given row. Each property, x, that the
  367.           // view gives back will cause the pseudoclass :moz-tree-row-x to be matched
  368.           // on the pseudoelement ::moz-tree-row. It is used here to color the background
  369.           // of a selected task with a color given by  the progress state of the task.
  370.           getRowProperties: function mTV_getRowProperties(aRow, aProps) {
  371.               var task = this.binding.mTaskArray[aRow];
  372.               aProps.AppendElement(getAtomFromService(this._progressAtom(task)));
  373.           },
  374.  
  375.           // Called on the view when a cell in a non-selectable cycling
  376.           // column (e.g., unread/flag/etc.) is clicked.
  377.           cycleCell: function mTV_cycleCell(aRow, aCol) {
  378.               var task = this.binding.mTaskArray[aRow];
  379.               if(!task)
  380.                   return;
  381.               if (aCol != null) {
  382.                   var anonid = aCol.element.getAttribute("anonid");
  383.                   if (anonid == "calendar-task-tree-col-completed")  {
  384.                       var newTask = task.clone().QueryInterface(Components.interfaces.calITodo);
  385.                       newTask.isCompleted = !task.completedDate;
  386.                       doTransaction('modify', newTask, newTask.calendar, task, null);
  387.                   }
  388.               }
  389.           },
  390.  
  391.           // Called on the view when a header is clicked.
  392.           cycleHeader: function mTV_cycleHeader(aCol) {
  393.               var element = aCol.element;
  394.               var sortActive = element.getAttribute("sortActive");
  395.               this.selectedColumn = element.getAttribute("anonid");  
  396.               this.sortDirection = element.getAttribute("sortDirection");
  397.  
  398.               var treeCols;
  399.               if (sortActive != "true") {
  400.                   var treecols = document.getAnonymousNodes(
  401.                       this.binding)[0].getElementsByTagName("treecol");
  402.                   for (var i = 0; i < treecols.length; i++) {
  403.                       treecols[i].removeAttribute("sortActive");
  404.                       treecols[i].removeAttribute("sortDirection");
  405.                   }
  406.                   sortActive = true;
  407.                   this.sortDirection = "ascending";
  408.               }
  409.               else {
  410.                   sortActive = true;
  411.                   if(this.sortDirection == "" || this.sortDirection == "descending") {
  412.                       this.sortDirection = "ascending";
  413.                   } else {
  414.                       this.sortDirection = "descending";
  415.                   }
  416.               }
  417.               element.setAttribute("sortActive", sortActive);
  418.               element.setAttribute("sortDirection", this.sortDirection);
  419.               this.sortStartedTime = now(); // for null dates during sort
  420.               this.binding.sortTaskArray();
  421.               this.binding.recreateHashTable();
  422.           },
  423.           
  424.           // The text for a given cell. If a column consists only of an
  425.           // image, then the empty string is returned.
  426.           getCellText: function mTV_getCellText(aRow, aCol) {
  427.               var task = this.binding.mTaskArray[aRow];
  428.               if (!task)
  429.                   return false;
  430.               switch(aCol.element.getAttribute("anonid")) {
  431.                   case "calendar-task-tree-col-title":
  432.                       // return title, or "Untitled" if empty/null
  433.                       return task.title || gCalendarBundle.getString("eventUntitled");
  434.                   case "calendar-task-tree-col-startdate":
  435.                       return this._formatDateTime(task.entryDate);
  436.                   case "calendar-task-tree-col-duedate":
  437.                       return this._formatDateTime(task.dueDate);
  438.                   case "calendar-task-tree-col-completeddate":
  439.                       return this._formatDateTime(task.completedDate);
  440.                   case "calendar-task-tree-col-percentcomplete":
  441.                       return task.percentComplete+"%";
  442.                   case "calendar-task-tree-col-categories":
  443.                       return task.getProperty("CATEGORIES");
  444.                   case "calendar-task-tree-col-location":
  445.                       return task.getProperty("LOCATION");
  446.                   case "calendar-task-tree-col-status":
  447.                       return getToDoStatusString(task);
  448.                   case "calendar-task-tree-col-calendarname":
  449.                       return task.calendar.name;
  450.                   case "calendar-task-tree-col-completed":
  451.                   case "calendar-task-tree-col-priority":
  452.                   default:
  453.                       return "";
  454.               }
  455.           },
  456.           
  457.           // This method is only called for columns of type other than text.
  458.           getCellValue: function mTV_getCellValue(aRow, aCol) {
  459.               return null;
  460.           },
  461.  
  462.           // SetCellValue is called when the value of the cell has been set by the user.
  463.           // This method is only called for columns of type other than text.
  464.           setCellValue: function mTV_setCellValue(aRow, aCol, aValue) {
  465.               return null;
  466.           },
  467.  
  468.           // The image path for a given cell. For defining an icon for a cell.
  469.           // If the empty string is returned, the :moz-tree-image pseudoelement will be used.
  470.           getImageSrc: function mTV_getImageSrc(aRow, aCol) {
  471.              // Return the empty string in order 
  472.              // to use moz-tree-image pseudoelement : 
  473.              // it is mandatory to return "" and not false :-(
  474.              return("");
  475.           },
  476.  
  477.           // IsEditable is called to ask the view if the cell contents are editable.
  478.           // A value of true will result in the tree popping up a text field when the user
  479.           // tries to inline edit the cell.
  480.           isEditable: function mTV_isEditable(aRow, aCol) {
  481.               return true;
  482.           },
  483.  
  484.           // Called during initialization to link the view to the front end box object.
  485.           setTree: function mTV_setTree(aTreeBox) {
  486.             this.treebox = aTreeBox;
  487.           },
  488.  
  489.           // Methods that can be used to test whether or not a twisty should
  490.           // be drawn, and if so, whether an open or closed twisty should be used.
  491.           isContainer: function mTV_isContainer(aRow) {
  492.               return false;
  493.           },
  494.  
  495.           // IsSeparator is used to determine if the row at index is a separator.
  496.           // A value of true will result in the tree drawing a horizontal separator.
  497.           // The tree uses the ::moz-tree-separator pseudoclass to draw the separator.
  498.           isSeparator: function mTV_isSeparator(aRow) {
  499.               return false;
  500.           },
  501.  
  502.           // Specifies if there is currently a sort on any column.
  503.           // Used mostly by dragdrop to affect drop feedback.
  504.           isSorted: function mTV_isSorted(aRow) {
  505.               return false;
  506.           },
  507.  
  508.           // The level is an integer value that represents the level of indentation.
  509.           // It is multiplied by the width specified in the :moz-tree-indentation
  510.           // pseudoelement to compute the exact indendation.
  511.           getLevel: function mTV_getLevel(aRow) {
  512.               return 0;
  513.           },
  514.           
  515.           canDrop: function mTV_canDrop() { return false; },
  516.           
  517.           /**
  518.            * Task Tree Events
  519.            */
  520.           onSelect: function tTV_onSelect(event) {},
  521.  
  522.           onDoubleClick: function tTV_onDoubleClick(event) {
  523.               if (event.button == 0) {
  524.                   var col = {};
  525.                   var item = this._getItemFromEvent(event, col);
  526.                   if (item) {
  527.                       var colAnonId = col.value.element.getAttribute("anonid");
  528.                       if (colAnonId == "calendar-task-tree-col-completed") {
  529.                           // item holds checkbox state toggled by first click,
  530.                           // so don't call modifyEventWithDialog
  531.                           // to make sure user notices state changed.
  532.                       } else {
  533.                           modifyEventWithDialog(item);
  534.                       }
  535.                   } else {
  536.                       createTodoWithDialog();
  537.                   }
  538.               }
  539.           },
  540.           
  541.           drop: function tTV_drop(aRow, aOrientation) {},
  542.           
  543.           getParentIndex: function tTV_getParentIndex(aRow) {
  544.               return -1;
  545.           },
  546.           
  547.           onKeyPress: function tTV_onKeyPress(event) {
  548.               const kKE = Components.interfaces.nsIDOMKeyEvent;
  549.               switch (event.keyCode || event.which) {
  550.                   case kKE.DOM_VK_DELETE:
  551.                       document.popupNode = this.binding;
  552.                       document.getElementById('calendar_delete_todo_command').doCommand();
  553.                       event.preventDefault();
  554.                       event.stopPropagation();
  555.                       break;
  556.                   case kKE.DOM_VK_SPACE:
  557.                       if (this.tree.currentIndex > -1) {
  558.                           var col = document.getAnonymousElementByAttribute(
  559.                               this.binding, "anonid", "calendar-task-tree-col-completed");
  560.                           this.cycleCell(
  561.                               this.tree.currentIndex,
  562.                               { element: col });
  563.                       }
  564.                       break;
  565.                   case kKE.DOM_VK_RETURN:
  566.                       var index = this.tree.currentIndex;
  567.                       if (index > 0) {
  568.                           modifyEventWithDialog(this.binding.mTaskArray[index]);
  569.                       }
  570.                       break;
  571.               }
  572.           },
  573.       
  574.           // Set the context menu on mousedown to change it before it is opened
  575.           onMouseDown: function tTV_onMouseDown(event) {
  576.             var tree = document.getAnonymousElementByAttribute(
  577.                 this.binding, "anonid", "calendar-task-tree");
  578.             var treechildren = tree.getElementsByTagName("treechildren")[0];
  579.             
  580.             if (!this._getItemFromEvent(event)) {
  581.                 tree.view.selection.clearSelection();
  582.             }
  583.           },
  584.           
  585.           /**
  586.            * Private methods and attributes
  587.            */
  588.           
  589.           _getItemFromEvent: function tTV_getItemFromEvent(event, aCol, aRow) {
  590.             aRow = aRow || {};
  591.             var childElt = {};
  592.             this.treebox.getCellAt(event.clientX, event.clientY, aRow, aCol || {}, childElt);
  593.             if (!childElt.value) {
  594.                 return false;
  595.             }
  596.             return aRow && aRow.value > -1 && this.binding.mTaskArray[aRow.value];
  597.           },
  598.           
  599.           // This function return the progress state of a task :
  600.           // completed, overdue, duetoday, inprogress, future
  601.           _progressAtom: function tTV_progressAtom(aTask) {
  602.               var now = new Date();
  603.  
  604.               if (aTask.isCompleted)
  605.                   return "completed";
  606.  
  607.               if (aTask.dueDate && aTask.dueDate.isValid) {
  608.                   if (aTask.dueDate.jsDate.getTime() < now.getTime())
  609.                       return "overdue";
  610.                   else if (aTask.dueDate.year == now.getFullYear() &&
  611.                            aTask.dueDate.month == now.getMonth() &&
  612.                            aTask.dueDate.day == now.getDate())
  613.                       return "duetoday";
  614.               }
  615.  
  616.               if (aTask.entryDate && aTask.entryDate.isValid &&
  617.                   aTask.entryDate.jsDate.getTime() < now.getTime())
  618.                   return "inprogress";
  619.  
  620.               return "future";
  621.           },
  622.           
  623.  
  624.           // Helper function to display datetimes
  625.           _formatDateTime: function tTV_formatDateTime(aDateTime) {
  626.               var dateFormatter = Components.classes["@mozilla.org/calendar/datetime-formatter;1"]
  627.                                             .getService(Components.interfaces.calIDateTimeFormatter);
  628.  
  629.               // datetime is from todo object, it is not a javascript date
  630.               if (aDateTime && aDateTime.isValid) {
  631.                   var dateTime = aDateTime.getInTimezone(calendarDefaultTimezone());
  632.                   return dateFormatter.formatDateTime(dateTime);
  633.               }
  634.               return "";
  635.           }
  636.       })
  637.       ]]></field>
  638.  
  639.       <!--
  640.         Observer for the calendar event data source. This keeps the unifinder
  641.         display up to date when the calendar event data is changed
  642.         -->
  643.       <field name="mTaskTreeObserver"><![CDATA[
  644.       ({
  645.           binding: this,
  646.           mInBatch: false,
  647.  
  648.           QueryInterface: function tTO_QueryInterface(aIID) {
  649.               ensureIID(
  650.                   [ Components.interfaces.calICompositeObserver,
  651.                     Components.interfaces.calIObserver,
  652.                     Components.interfaces.nsISupports], aIID);
  653.               return this;
  654.           },
  655.  
  656.          /**
  657.            * calIObserver methods and properties
  658.            */
  659.           onStartBatch: function tTO_onStartBatch() {
  660.               this.mInBatch = true;
  661.           },
  662.           
  663.           onEndBatch: function tTO_onEndBatch() {
  664.               if (this.mInBatch) {
  665.                   this.mInBatch = false;
  666.                   this.binding.refresh();
  667.               }
  668.           },
  669.           
  670.           onLoad: function tTO_onLoad() {
  671.               if (!this.mInBatch) {
  672.                   this.binding.refresh();
  673.               }
  674.           },
  675.           
  676.           onAddItem: function tTO_onAddItem(aItem) {
  677.               if (aItem instanceof Components.interfaces.calITodo && !this.mInBatch) {
  678.                   this.binding.mTreeView.addItem(aItem);
  679.               }
  680.           },
  681.           
  682.           onModifyItem: function tTO_onModifyItem(aNewItem, aOldItem) {
  683.               if ((aNewItem instanceof Components.interfaces.calITodo ||
  684.                   aOldItem instanceof Components.interfaces.calITodo) &&
  685.                   !this.mInBatch) {
  686.                   
  687.                   // forward the call to the view which will in turn
  688.                   // update the internal reference and the view.
  689.                   this.binding.mTreeView.modifyItem(aNewItem, aOldItem);
  690.                   
  691.                   // we also need to notify potential listeners.
  692.                   var event = document.createEvent('Events');
  693.                   event.initEvent('select', true, false);
  694.                   this.binding.dispatchEvent(event);
  695.               }
  696.           },
  697.           
  698.           onDeleteItem: function tTO_onDeleteItem(aDeletedItem) {
  699.               if (aDeletedItem instanceof Components.interfaces.calITodo &&
  700.                   !this.mInBatch) {
  701.                   this.binding.mTreeView.removeItem(aDeletedItem);  
  702.               }
  703.           },
  704.           
  705.           onError: function tTO_onError(aErrNo, aMessage) {},
  706.           onPropertyChanged: function tTO_onPropertyChanged(aCalendar, aName, aValue, aOldValue) {},
  707.           onPropertyDeleting: function tTO_onPropertyDeleting(aCalendar, aName) {},
  708.  
  709.           /**
  710.            * calICompositeObserver methods and properties
  711.            */
  712.           onCalendarAdded: function tTO_onCalendarAdded(aCalendar) {
  713.               if (!this.mInBatch) {
  714.                   this.binding.onCalendarAdded(aCalendar);
  715.               }
  716.           },
  717.           
  718.           onCalendarRemoved: function tTO_onCalendarRemoved(aCalendar) {
  719.               if (!this.mInBatch) {
  720.                   this.binding.onCalendarRemoved(aCalendar);
  721.               }
  722.           },
  723.           
  724.           onDefaultCalendarChanged: function tTO_onDefaultCalendarChanged(aNewDefaultCalendar) {}
  725.       })
  726.       ]]></field>
  727.       
  728.       <constructor><![CDATA[
  729.         var self = this;
  730.         var load = function tTV_loadHandler() {
  731.             self.onLoad();
  732.         };
  733.         window.addEventListener("load", load, true);
  734.         var unload = function tTV_unloadHandler() {
  735.             self.onUnload();
  736.         };
  737.         window.addEventListener("unload", unload, true);
  738.  
  739.         // we want to make several attributes on the column
  740.         // elements persistent, but unfortunately there's no
  741.         // relyable way with the 'persist' feature.
  742.         // that's why we need to store the necessary bits and
  743.         // pieces at the element this binding is attached to.
  744.         var names = this.getAttribute("visible-columns").split(' ');
  745.         var ordinals = this.getAttribute("ordinals").split(' ');
  746.         var widths = this.getAttribute("widths").split(' ');
  747.         var sortActive = this.getAttribute("sort-active");
  748.         var sortDirection = this.getAttribute("sort-direction") || "ascending";
  749.         var treecols = document.getAnonymousNodes(
  750.             this)[0].getElementsByTagName("treecol");
  751.         for (var i = 0; i < treecols.length; i++) {
  752.             var anonid = treecols[i].getAttribute("anonid");
  753.             if (names.some(
  754.                 function(element) {
  755.                     var pos = anonid.length - element.length;
  756.                     return (anonid.indexOf(element) == pos);
  757.                 })) {
  758.                 treecols[i].removeAttribute("hidden");
  759.             } else {
  760.                 treecols[i].setAttribute("hidden","true");
  761.             }
  762.             if (ordinals && ordinals.length > 0) {
  763.                treecols[i].ordinal = Number(ordinals.shift());
  764.             }
  765.             if (widths && widths.length > 0) {
  766.                treecols[i].width = Number(widths.shift());
  767.             }
  768.             if (sortActive && sortActive.length > 0) {
  769.                 var pos = anonid.length - sortActive.length;
  770.                 if (anonid.indexOf(sortActive) == pos) {
  771.                     treecols[i].setAttribute("sortActive", "true");
  772.                     treecols[i].setAttribute("sortDirection", sortDirection);
  773.                 }
  774.             }
  775.         }
  776.       ]]></constructor>
  777.  
  778.       <method name="onLoad">
  779.         <body><![CDATA[
  780.           if (!this.mLoadCount++) {
  781.  
  782.               // set up the custom tree view
  783.               var tree = document.getAnonymousElementByAttribute(this, "anonid", "calendar-task-tree");
  784.               this.mTreeView.tree = tree;
  785.               tree.view = this.mTreeView;
  786.           
  787.               // set up our calendar event observer
  788.               var composite = getCompositeCalendar();
  789.               composite.addObserver(this.mTaskTreeObserver);
  790.           }
  791.         ]]></body>
  792.       </method>
  793.  
  794.       <method name="onUnload">
  795.         <body><![CDATA[
  796.           if (!--this.mLoadCount) {
  797.               // remove out calendar event observer
  798.               var composite = getCompositeCalendar();
  799.               composite.removeObserver(this.mTaskTreeObserver);
  800.               
  801.               var widths = "";
  802.               var ordinals = "";
  803.               var visible = "";
  804.               var sortActive = null;
  805.               var sortDirection = null;
  806.               var treecols = document.getAnonymousNodes(
  807.                   this)[0].getElementsByTagName("treecol");
  808.               for (var i = 0; i < treecols.length; i++) {
  809.                   if (treecols[i].getAttribute("hidden") != "true") {
  810.                     var anonid = treecols[i].getAttribute("anonid");
  811.                     anonid = anonid.substr(anonid.lastIndexOf("-")+1);
  812.                     visible += (visible.length > 0) ? " "+anonid : anonid;
  813.                   }
  814.                   if(ordinals.length > 0)
  815.                       ordinals += " ";
  816.                   ordinals += treecols[i].ordinal;
  817.                   if(widths.length > 0)
  818.                       widths += " ";
  819.                   widths += treecols[i].width || 0;
  820.  
  821.                   if (treecols[i].getAttribute("sortActive") == "true") {
  822.                     sortActive = treecols[i].getAttribute("anonid");
  823.                     sortActive = sortActive.substr(sortActive.lastIndexOf("-")+1);
  824.                     sortDirection = treecols[i].getAttribute("sortDirection");
  825.                   }
  826.  
  827.               }
  828.               this.setAttribute("visible-columns",visible);
  829.               this.setAttribute("ordinals",ordinals);
  830.               this.setAttribute("widths",widths);
  831.               if (sortActive) {
  832.                   this.setAttribute("sort-active",sortActive);
  833.                   this.setAttribute("sort-direction",sortDirection);
  834.               } else {
  835.                   this.removeAttribute("sort-active");
  836.                   this.removeAttribute("sort-direction");
  837.               }
  838.           }
  839.         ]]></body>
  840.       </method>
  841.  
  842.       <!-- Called by event observers to update the display -->
  843.       <method name="refresh">
  844.         <parameter name="aFilter"/>
  845.         <body><![CDATA[
  846.           var savedThis = this;
  847.           var refreshListener = {
  848.               mTaskArray: [],
  849.  
  850.               onOperationComplete: function(aCalendar, aStatus, aOperationType, aId, aDateTime) {
  851.                   savedThis.mTaskArray = this.mTaskArray;
  852.                   savedThis.onOperationComplete();
  853.              },
  854.  
  855.              onGetResult: function(aCalendar, aStatus, aItemType, aDetail, aCount, aItems) {
  856.                  for (var i=0; i<aCount; i++) {
  857.                      if (!savedThis.mFilterFunction ||
  858.                          savedThis.mFilterFunction(aItems[i])) {
  859.                          refreshListener.mTaskArray.push(aItems[i]);
  860.                      }
  861.                  }
  862.              }
  863.           };
  864.  
  865.           var refreshJob = {
  866.               execute: function() {
  867.                   var composite = getCompositeCalendar();
  868.                   var filter = aFilter || savedThis.mHideCompletedTasks ?
  869.                       composite.ITEM_FILTER_COMPLETED_NO :
  870.                       composite.ITEM_FILTER_COMPLETED_ALL;
  871.                   filter |= composite.ITEM_FILTER_TYPE_TODO;
  872.                   if (savedThis.startDate && savedThis.endDate) {
  873.                       filter |= composite.ITEM_FILTER_CLASS_OCCURRENCES;
  874.                   }
  875.                   composite.getItems(filter, 0, savedThis.startDate, savedThis.endDate, refreshListener);
  876.               }
  877.           };
  878.           this.mRefreshQueue.push(refreshJob);
  879.           this.popRefreshQueue();
  880.         ]]></body>
  881.       </method>
  882.  
  883.       <method name="onCalendarAdded">
  884.         <parameter name="aCalendar"/>
  885.         <parameter name="aFilter"/>
  886.         <body><![CDATA[
  887.           var savedThis = this;
  888.           var refreshListener = {
  889.               onOperationComplete: function (aCalendar, aStatus, aOperationType, aId, aDateTime) {
  890.                   savedThis.onOperationComplete();
  891.              },
  892.  
  893.              onGetResult: function (aCalendar, aStatus, aItemType, aDetail, aCount, aItems) {
  894.                  for (var i=0; i<aCount; i++) {
  895.                      if (!savedThis.mFilterFunction ||
  896.                          savedThis.mFilterFunction(aItems[i])) {
  897.                          savedThis.mTaskArray.push(aItems[i]);
  898.                      }
  899.                  }
  900.              }
  901.           };
  902.           
  903.           var refreshJob = {
  904.               execute: function() {
  905.                   var composite = getCompositeCalendar();
  906.                   var filter = aFilter || savedThis.mHideCompletedTasks ?
  907.                       composite.ITEM_FILTER_COMPLETED_NO :
  908.                       composite.ITEM_FILTER_COMPLETED_ALL;
  909.                   filter |= composite.ITEM_FILTER_TYPE_TODO;
  910.                   if (savedThis.startDate && savedThis.endDate) {
  911.                       filter |= composite.ITEM_FILTER_CLASS_OCCURRENCES;
  912.                   }
  913.                   aCalendar.getItems(filter, 0, savedThis.startDate, savedThis.endDate, refreshListener);
  914.               }
  915.           };
  916.           this.mRefreshQueue.push(refreshJob);
  917.           this.popRefreshQueue();
  918.         ]]></body>
  919.       </method>
  920.       
  921.       <method name="onCalendarRemoved">
  922.         <parameter name="aCalendar"/>
  923.         <body><![CDATA[
  924.           if (this.mTaskArray.length > 0) {
  925.               var index = this.mTaskArray.length - 1;
  926.               while(index >= 0) {
  927.                   var item = this.mTaskArray[index];
  928.                   if (item.calendar == aCalendar) {
  929.                       this.mTreeView.removeItem(item);  
  930.                   }
  931.                   --index;
  932.               }
  933.           }
  934.  
  935.           // we also need to notify potential listeners.
  936.           var event = document.createEvent('Events');
  937.           event.initEvent('select', true, false);
  938.           this.dispatchEvent(event);
  939.         ]]></body>
  940.       </method>
  941.  
  942.       <method name="popRefreshQueue">
  943.         <body><![CDATA[
  944.           var pendingRefresh = this.mPendingRefresh;
  945.           if (pendingRefresh) {
  946.               if (pendingRefresh instanceof Components.interfaces.calIOperation) {
  947.                   this.mPendingRefresh = null;
  948.                   pendingRefresh.cancel(null);
  949.               } else {
  950.                   return;
  951.               }
  952.           }
  953.  
  954.           var refreshJob = this.mRefreshQueue.pop();
  955.           if (!refreshJob) {
  956.               return;
  957.           }
  958.  
  959.           this.mPendingRefresh = true;
  960.           pendingRefresh = refreshJob.execute();
  961.           if (pendingRefresh && pendingRefresh.isPending) {
  962.               this.mPendingRefresh = pendingRefresh;
  963.           }
  964.         ]]></body>
  965.       </method>
  966.       
  967.       <method name="onOperationComplete">
  968.         <body><![CDATA[
  969.           // signal that the current operation finished.
  970.           this.mPendingRefresh = null;
  971.         
  972.           // immediately start the next job on the queue.
  973.           this.popRefreshQueue();
  974.         
  975.           this.mTreeView.rowCount = this.mTaskArray.length;
  976.           var treecols = document.getAnonymousNodes(this)[0].getElementsByTagName("treecol");
  977.           for (var i = 0; i < treecols.length; i++) {
  978.             if (treecols[i].getAttribute("sortActive") == "true") {
  979.                this.mTreeView.selectedColumn = treecols[i].getAttribute("anonid");
  980.                this.mTreeView.sortDirection = treecols[i].getAttribute("sortDirection");
  981.                this.mTreeView.sortStartedTime = now(); //for null/0 dates
  982.                this.sortTaskArray();
  983.                break;
  984.             }
  985.           }
  986.           this.recreateHashTable();
  987.           
  988.           var tree = document.getAnonymousElementByAttribute(
  989.               this, "anonid", "calendar-task-tree");
  990.           tree.view = this.mTreeView;
  991.  
  992.           // we also need to notify potential listeners.
  993.           var event = document.createEvent('Events');
  994.           event.initEvent('select', true, false);
  995.           this.dispatchEvent(event);
  996.         ]]></body>
  997.       </method>
  998.       
  999.       <method name="sortTaskArray">
  1000.         <body><![CDATA[
  1001.           var kStatusOrder = [
  1002.               "NEEDS-ACTION",
  1003.               "IN-PROCESS",
  1004.               "COMPLETED",
  1005.               "CANCELLED" ];
  1006.           function compareString(a, b) {
  1007.               a = nullToEmpty(a);
  1008.               b = nullToEmpty(b);
  1009.               return ((a < b) ? -1 :
  1010.                       (a > b) ?  1 : 0);
  1011.           }
  1012.  
  1013.           function nullToEmpty(value) {
  1014.               return value == null? "" : value;
  1015.           }
  1016.  
  1017.           function compareNumber(a, b) {
  1018.               // Number converts a date to msecs since 1970, and converts a null to 0.
  1019.               a = Number(a); 
  1020.               b = Number(b);
  1021.               return ((a < b) ? -1 :      // avoid underflow problems of subtraction
  1022.                       (a > b) ?  1 : 0); 
  1023.           }
  1024.  
  1025.           var sortStartedTime = this.mTreeView.sortStartedTime;
  1026.           var sortDirection = this.mTreeView.sortDirection;
  1027.           var selectedColumn = this.mTreeView.selectedColumn;
  1028.  
  1029.           // Takes two calDateTimes
  1030.           function compareDate(a, b) {
  1031.               if (!a)
  1032.                   a = sortStartedTime;
  1033.               if (!b)
  1034.                   b = sortStartedTime;
  1035.               return a.compare(b);
  1036.           }
  1037.                   
  1038.           function compareItems(a, b) {
  1039.               var modifier = (sortDirection == "descending") ? -1 : 1;
  1040.               switch(selectedColumn) {
  1041.                   case "calendar-task-tree-col-priority":  // 0-9
  1042.                       // No priority set (0) sorts before high priority (1).
  1043.                       return compareNumber(a.priority, b.priority) * modifier;
  1044.                   case "calendar-task-tree-col-title":
  1045.                       return compareString(a.title.toLowerCase(), b.title.toLowerCase()) * modifier;
  1046.                   case "calendar-task-tree-col-startdate":
  1047.                       return compareDate(a.entryDate, b.entryDate) * modifier;
  1048.                   case "calendar-task-tree-col-duedate":
  1049.                       return compareDate(a.dueDate, b.dueDate) * modifier;
  1050.                   case "calendar-task-tree-col-completed": // checkbox if date exists
  1051.                   case "calendar-task-tree-col-completeddate":
  1052.                       return compareDate(a.completedDate, b.completedDate) * modifier;
  1053.                   case "calendar-task-tree-col-percentcomplete":
  1054.                       return compareNumber(a.percentComplete, b.percentComplete) * modifier;
  1055.                   case "calendar-task-tree-col-categories":
  1056.                       return compareString(a.getProperty("CATEGORIES"), b.getProperty("CATEGORIES")) * modifier;
  1057.                   case "calendar-task-tree-col-location":
  1058.                       return compareString(a.getProperty("LOCATION"), b.getProperty("LOCATION")) * modifier;
  1059.                   case "calendar-task-tree-col-status":
  1060.                       return compareNumber(kStatusOrder.indexOf(a.status), kStatusOrder.indexOf(b.status)) * modifier;
  1061.                   case "calendar-task-tree-col-calendarname":
  1062.                       return compareString(a.calendar.name, b.calendar.name) * modifier;
  1063.                   default:
  1064.                       return 0;
  1065.               }
  1066.           }
  1067.         
  1068.           // use above local defined functor
  1069.           this.mTaskArray.sort(compareItems);
  1070.           var tree = document.getAnonymousElementByAttribute(this, "anonid", "calendar-task-tree");
  1071.           if (tree.boxObject.invalidateRange) {
  1072.               tree.boxObject.invalidateRange(0, this.mTaskArray.length - 1);
  1073.           }
  1074.         ]]></body>
  1075.       </method>
  1076.       
  1077.       <method name="recreateHashTable">
  1078.         <body><![CDATA[
  1079.           this.mHash2Index = {};
  1080.           for (var i=0; i<this.mTaskArray.length; i++) {
  1081.               var item = this.mTaskArray[i];
  1082.               this.mHash2Index[item.hashId] = i;
  1083.           }
  1084.         ]]></body>
  1085.       </method>
  1086.       
  1087.     </implementation>
  1088.  
  1089.     <handlers>
  1090.       <handler event="select"><![CDATA[
  1091.         this.mTreeView.onSelect(event);
  1092.       ]]></handler>
  1093.       <handler event="dblclick" button="0"><![CDATA[
  1094.         this.mTreeView.onDoubleClick(event);
  1095.       ]]></handler>
  1096.       <handler event="keypress"><![CDATA[
  1097.         this.mTreeView.onKeyPress(event);
  1098.       ]]></handler>
  1099.       <handler event="mousedown"><![CDATA[
  1100.         this.mTreeView.onMouseDown(event);
  1101.       ]]></handler>
  1102.       <handler event="draggesture"><![CDATA[
  1103.         var item = this.mTreeView._getItemFromEvent(event);
  1104.         if (!item || item.calendar.readOnly) {
  1105.             return;
  1106.         }
  1107.  
  1108.         var tree = document.getAnonymousElementByAttribute(this, "anonid", "calendar-task-tree");
  1109.         
  1110.         // let's build the drag region
  1111.         var region = null;
  1112.         try {
  1113.           region = Components.classes["@mozilla.org/gfx/region;1"].createInstance(Components.interfaces.nsIScriptableRegion);
  1114.           region.init();
  1115.           var obo = tree.treeBoxObject;
  1116.           var bo = obo.treeBody.boxObject;
  1117.           var sel= tree.view.selection;
  1118.  
  1119.           var rowX = bo.x;
  1120.           var rowY = bo.y;
  1121.           var rowHeight = obo.rowHeight;
  1122.           var rowWidth = bo.width;
  1123.  
  1124.           //add a rectangle for each visible selected row
  1125.           for (var i = obo.getFirstVisibleRow(); i <= obo.getLastVisibleRow(); i ++) {
  1126.             if (sel.isSelected(i))
  1127.               region.unionRect(rowX, rowY, rowWidth, rowHeight);
  1128.             rowY = rowY + rowHeight;
  1129.           }
  1130.           
  1131.           //and finally, clip the result to be sure we don't spill over...
  1132.           if(!region.isEmpty())
  1133.             region.intersectRect(bo.x, bo.y, bo.width, bo.height);
  1134.         } catch(ex) {
  1135.           ASSERT(false, "Error while building selection region: " + ex + "\n");
  1136.           region = null;
  1137.         }
  1138.         
  1139.         var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].
  1140.                           getService(Components.interfaces.nsIDragService);
  1141.         var transfer = Components.classes["@mozilla.org/widget/transferable;1"].
  1142.                        createInstance(Components.interfaces.nsITransferable);
  1143.         transfer.addDataFlavor("text/calendar");
  1144.  
  1145.         var serializer = Components.classes["@mozilla.org/calendar/ics-serializer;1"].
  1146.                                     createInstance(Components.interfaces.calIIcsSerializer);
  1147.         serializer.addItems([item], 1);
  1148.         
  1149.         var supportsString = Components.classes["@mozilla.org/supports-string;1"].
  1150.                              createInstance(Components.interfaces.nsISupportsString);
  1151.         supportsString.data = serializer.serializeToString();
  1152.         transfer.setTransferData("text/calendar", supportsString, supportsString.data.length*2);
  1153.         transfer.setTransferData("text/unicode", supportsString, supportsString.data.length*2);
  1154.  
  1155.         var action = dragService.DRAGDROP_ACTION_MOVE;
  1156.         var supArray = Components.classes["@mozilla.org/supports-array;1"].
  1157.                        createInstance(Components.interfaces.nsISupportsArray);
  1158.         supArray.AppendElement(transfer);
  1159.  
  1160.         dragService.invokeDragSession(event.target, supArray, region, action);
  1161.       ]]></handler>
  1162.     </handlers>
  1163.  
  1164.   </binding>
  1165.  
  1166. </bindings>
  1167.